package com.hrt.datasender.datacollect.drive.opc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.btcode.common.MyLog;
import com.btcode.log.ILog;
import com.btcode.schedule.ScheduleManager;
import com.hrt.datasender.datacollect.drive.DriveConfigModel;
import com.hrt.datasender.datacollect.drive.DriveParameterMetadata;
import com.hrt.datasender.datacollect.drive.IDrive;
import com.hrt.datasender.datadao.model.BcDeviceConfigModel;
import com.hrt.datasender.datadao.model.BcDeviceMonitorParamModel;
import com.hrt.datasender.datadao.model.BcDevicesModel;
import com.hrt.datasender.datadao.model.BdSourceModel;
import com.hrt.datasender.mq.MqKit;
import com.hrt.datasender.mq.MqKit.MsgListener;
import com.hrt.datasender.mq.model.MqModel;
import com.hrt.datasender.mq.model.OpcModel;

public class OpcDrive implements IDrive {

    protected ILog log;

    private List<BdSourceModel> sourceData;

    private BcDevicesModel device;

    public static HashMap<String,OpcDataClient> opcDataReaderMap = new HashMap<>();

    private DriveConfigModel configs = new DriveConfigModel();
    
    private final int doubleScale = 3;

    @Override
    public void init(BcDevicesModel device, List<BcDeviceMonitorParamModel> params, List<BcDeviceConfigModel> configs) {
        try {
            log = MyLog.getInstance().getLogger(getClass().getName() + "." + device.getDeviceName());

            log.info("获取设备参数信息：" + params);
            log.info("获取设备信息：" + device);

            this.device = device;

            HashMap<String, String> configMap = new HashMap<>();

            /**
             * 把参数列表转换成map，方便报文解析转换处理
             */
            HashMap<String, String> paramCodeMap = new HashMap<>();
            for (BcDeviceMonitorParamModel parm : params) {
                paramCodeMap.put(parm.getParamCodePro(), parm.getParamCodeBus());
            }

            /**
             * 把配置列表转换成map，方便处理
             */
            for (BcDeviceConfigModel parm : configs) {
                configMap.put(parm.getName(), parm.getValue());
            }

            this.configs.setConfig(configMap);
            this.configs.setName(this.device.getDeviceName());
            this.configs.put("name", this.device.getDeviceName());

            /**
             * 创建OpcData客户端
             */
            String serverID = configMap.get("serverID");
            String clientHandle = configMap.get("clientHandle");
            String groupName = configMap.get("groupName");
            OpcDataClient opcDataReader = createOpcDataGroup(params,groupName,serverID,clientHandle);
            opcDataReaderMap.put(this.device.getDeviceName(),opcDataReader);

            /**
             * 注册消息监听
             */
            registerMQListener(device, paramCodeMap, this.configs);

            /**
             * 注册数据询问定时器
             */
            registerAskDataJob(device, this.configs, paramCodeMap);

            log.info("设备初始化完成：" + device.getDeviceName());
        }
        catch (Exception e) {
            log.error("初始化驱动出错", e);
        }

    }

    /**
     * 监听opc wencheng 2018年7月30日
     */
    public OpcDataClient createOpcDataGroup(List<BcDeviceMonitorParamModel> params,String groupName,
                                          String severId,String clientHandle) {
        // PARAM_CODE_PRO：OPC编码
        List<String> itemNames = new ArrayList<String>();
        for (int i = 0; i < params.size(); i++) {
            itemNames.add(params.get(i).getParamCodePro());
        }
        OpcDataClient opcDataReader = new OpcDataClient(itemNames, groupName,severId,clientHandle);
        return opcDataReader;
    }

    /**
     * 注册消息监听，调用子类的getSourceDataFromDevice方法解析数据
     */
    protected void registerMQListener(BcDevicesModel device, HashMap<String, String> paramCodeMap,
            DriveConfigModel configs) {

        MqKit.consumeMsgFromOpcMQ(device, new MsgListener() {

            @Override
            public void onReceive(MqModel mqModel) {
                OpcModel model = (OpcModel) mqModel;
                model.getData();
                List<BdSourceModel> datas = processOpcData(model.getData(), paramCodeMap, device, configs);
                log.info("数据解析成功，size:" + datas.size());
                setSourceData(datas);
            }
        });
    }

    protected void registerAskDataJob(BcDevicesModel device, DriveConfigModel configs,
            HashMap<String, String> paramCodeMap) {
        // 注册opc数据读取定时任务
        HashMap<String, Object> data = new HashMap<>();
        data.put("device", device);
        data.put("deviceConfigs", configs);
        String jobName = getClass().getName() + "." + device.getId();
        ScheduleManager.getInstance().addJob(OpcDataAsker.class, getConsStr(), data, jobName);
    }

    @Override
    public String getConsStr() {
        return "0/30 * * * * ?";
    }

    @Override
    public String getDriveName() {
        return "OPC驱动";
    }

    @Override
    public List<DriveParameterMetadata> getParameterMetadata() {
        List<DriveParameterMetadata> params = new ArrayList<>();
        params.add(new DriveParameterMetadata("serverID", "serverID"));
        params.add(new DriveParameterMetadata("clientHandle", "clientHandle"));
        params.add(new DriveParameterMetadata("groupName", "groupName"));
        return params;
    }

    @Override
    public String getNote() {
        // TODO Auto-generated method stub
        return null;
    }

    public List<BdSourceModel> processOpcData(HashMap<String, String> opcData, HashMap<String, String> paramCodeMap,
            BcDevicesModel deviceData, DriveConfigModel configs) {
        log = MyLog.getInstance().getLogger(getClass().getName() + "." + deviceData.getDeviceName());

        List<BdSourceModel> sourceModels = new ArrayList<>();

        Set<String> keys = opcData.keySet();
        Iterator<String> iterator = keys.iterator();


        Iterator<String> test = paramCodeMap.keySet().iterator();
        while (test.hasNext()){
            String keyTest = test.next();
            String valueTest = paramCodeMap.get(keyTest);
            log.info("keyTest" + keyTest+",valueTest"+valueTest);
        }

        while (iterator.hasNext()) {
            String key = iterator.next();
            log.info("key" + key);
            BdSourceModel sourModel = new BdSourceModel();
            String paramCodeBus = paramCodeMap.get(key);
            log.info("paramCodeBus" + paramCodeBus);
            sourModel.setParamCodeBus(paramCodeBus);
            String value = opcData.get(key);
            BigDecimal doubleValue = new BigDecimal(value);
            double measD = doubleValue.setScale(doubleScale, BigDecimal.ROUND_HALF_EVEN).doubleValue();
            sourModel.setMeasRe(measD);
            sourModel.setDatatime(new Date());
            sourceModels.add(sourModel);

        }
        return sourceModels;
    }

    @Override
    public List<BdSourceModel> getCurrentData() {

        List<BdSourceModel> dataGet = getSourceData();

        if (dataGet == null) {
            return null;
        }

        synchronized (getClass()) {
            try {
                List<BdSourceModel> returnResult = deepCopy(dataGet);
                /**
                 * 置空，下次循环如果没有新数据就会自动忽略
                 */
                setSourceData(null);
                return returnResult;
            }
            catch (ClassNotFoundException | IOException e) {
                log.error("拷贝数据出错", e);
            }
        }

        return null;

    }

    private List<BdSourceModel> getSourceData() {
        return sourceData;
    }

    protected void setSourceData(List<BdSourceModel> sourceData) {
        this.sourceData = sourceData;
    }

    /**
     * 深拷贝数据
     * 
     * @param src
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }

    public String toString() {
        StringBuffer strBuf = new StringBuffer("driveClass:" + this.getClass().getName() + ",");

        if (device != null) {
            strBuf.append("DeviceName:" + device.getDeviceName());
        }

        return strBuf.toString();
    }
}